%{
#include <stdio.h>
#include <string.h>
#include "table.h"
#include "coolinary.h"

static table symboltable;
static table nutritiontable;

extern FILE* yyin;


void coolinary_init(void) {
        nutritiontable = table_init(20);

        cooltype water = { .name = "TEMPLATE", .type = "water", .nutrition = 35, .quantity = 1, .time = 0 };
        cooltype dairy = { .name = "TEMPLATE", .type = "dairy", .nutrition = 10, .quantity = 1, .time = 0 };
        cooltype vegetable = { .name = "TEMPLATE", .type = "vegetable", .nutrition = 50, .quantity = 1, .time = 0 };
        cooltype fruit = { .name = "TEMPLATE", .type = "fruit", .nutrition = 50, .quantity = 1, .time = 0 };
        cooltype meat = { .name = "TEMPLATE", .type = "meat", .nutrition = 20, .quantity = 1, .time = 0 };
        cooltype liquid = { .name = "TEMPLATE", .type = "liquid", .nutrition = 5, .quantity = 1, .time = 0 };
        cooltype grain = { .name = "TEMPLATE", .type = "grain", .nutrition = 45, .quantity = 1, .time = 0 };

        int varsz = sizeof(cooltype);

        table_insert(nutritiontable,"water", FGROUP, &water, varsz);
        table_insert(nutritiontable,"dairy",FGROUP, &dairy, varsz);
        table_insert(nutritiontable,"vegetable",FGROUP,&vegetable, varsz);
        table_insert(nutritiontable,"fruit",FGROUP,&fruit,varsz);
        table_insert(nutritiontable,"meat",FGROUP,&meat,varsz);
        table_insert(nutritiontable,"liquid",FGROUP,&liquid,varsz);
        table_insert(nutritiontable,"grain",FGROUP,&grain,varsz);

	symboltable = table_init(50);
}

void yyerror(const char *str) { fprintf(stderr,"error: %s\n",str); }

/*_____________________________________________new structs in coolinary.h_____________________________________________*/

/*_____________________________________________execute______________________________________________________________*/

	/*function that executes all of the statments in a statement list*/
	void execute (struct statement_list stmt_list) {
		int i=0;

		/*all functions are void with 1 parameter - of type struct params_list - so the call is the same*/
		for (i;i<stmt_list.num_statements;i++) {
			stmt_list.s_list[i].fpointer (stmt_list.s_list[i].p_list);
		}
	}

/*_____________________________________________helper functions________________________________________________*/
	
	/*converts a list of ids (strings) to a list of params */
	/*could do the id checking here instead of waiting - but we don't */
	struct param_list ids_to_params (struct id_list ids)
	{
		
		/*allocate space*/	
		struct param_list *new = (struct param_list*) malloc (sizeof (struct param_list));

		/*"convert" from strings to addresses*/
		int i = 0;
		for (i;i<ids.num_ids;i++) {
			new->list[i].value = (int) ids.list[i];
		}

		/*set number*/
		new->num_params = ids.num_ids;

		/*maybe should return a pointer instead?*/
		return *new;
	}

	/*gets an address from a variable or gives an error if the variable does not exist or is of the wrong type*/
	rh_address* get_address_size (char* name, int typename)
	{
		lookup_result *tl = table_includes(symboltable, name);

		if (tl->index == -1) {
			yyerror("Compile-Time Error");
			printf("Variable %s is not declared before use.\n", name);
			exit(-1);
		}
		else if (tl->type != typename) {
			yyerror("Compile-Time Error");
			printf("Type mismatch with regards to variable %s.\n", name);
			exit(-1);
		}

		return table_retrieve (symboltable, tl->index);
	}

	/*gets the type and address for a variable - or gives an error if it doesn't exist.  Is returned using a lh_result struct - which
	contains an enum type and a pointer to void*/

	int get_type (char* name) {
		lookup_result *tl = table_includes(symboltable, name);

		if (tl->index == -1) {
			yyerror("Compile-Time Error");
			printf("Variable %s is not defined before use.\n", name);
			exit(-1);
		}

		return tl->type;
	}

	/*returns a string from an integer*/
	char* itoa (int a) 
	{
		int length = 1;
		int i, count;


		{for (i=10;((i-.01) < a);i*=10,length++);}

		char* new = (char*) malloc (length+1);

		for (count = 0, i /= 10; count<length;count++,i/=10) {
			*(new + count) = (a / i) + 48;
			a -= (a / i) * i;
		}

		*(new + length) = '\0';

		return new;
	}	

	/*returns a specified value from a cool type object*/
	int get_cool_value (char* name, int field)
	{
		rh_address *rh_addr = get_address_size (name,FGROUP);
		switch (field) {
			case FNAME:  return (int) ((cooltype*) rh_addr->addr)->name; break;
		}
	}

		
				

/*_____________________________________________expression functions_______________________________________________*/

	/*two parameters - can be values or variables - can't be other operations
	 we don't have chaining/nesting of operations */
	
	int int_evaluate (struct param_list params, int op) {
		int vals[2];
		int i = 0;
		int v_type;

		for (i = 0; i<2; i++) {
			if (params.list[i].is_name) {
				v_type = get_type ((char*) params.list[i].value);

				/*could be be string concat*/
				if (i == 0 && v_type == CSTRING) {return -1;}

				/*error*/
				else if (v_type != INTEGER) {
					yyerror ("Coolinary binary operators are limited to integer types.\n");
					exit (-1);
				}

				rh_address* temp = get_address_size ((char*) params.list[i].value, INTEGER);
				vals[i] = *((int*) temp->addr);
			}
			else if (params.list[i].is_accsr) {
				struct param_list *accsr = (struct param_list*) params.list[i].value;
				
				/*could be string concat*/
				if (i==0 && accsr->list[1].value == FNAME) {
					return -1;
				}
				else {
					rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP);
					switch (accsr->list[1].value) {
						case FNAME:  yyerror ("name cannot be used as an operand in an integer context"); exit(-1); break;
						case FQUANTITY: vals[i] = ((cooltype *) rh_addr->addr)->quantity; break;
						case FNUTRITION: vals[i] = ((cooltype *) rh_addr->addr)->nutrition; break;
						case FTIME: vals[i] = ((cooltype *) rh_addr->addr)->time; break;			
					}
				}
			}
				
			else {
				/*test for possible string concat*/
				if (i == 0 && params.list[0].type == CSTRING) {return -1;}
				else if (params.list[i].type != INTEGER) {
					yyerror ("Coolinary binary operators are limited to integer types.\n");
					exit (-1);
				}
				vals[i] = params.list[i].value;
			}
		}

		switch (op) {
			case ADD: return (vals[0] + vals[1]); break;
			case SUBTRACT: return (vals[0] - vals[1]); break;
			case MULTIPLY: return (vals[0] * vals[1]); break;
			case DIVIDE: return (vals[0] / vals[1]); break;
			case LTHAN: return (vals[0] < vals[1]); break;
			case LTHANE: return (vals[0] <= vals[1]); break;
			case EQUALS: return (vals[0] == vals[1]); break;
			case NEQUALS: return (vals[0] != vals[1]); break;
			case GTHANE: return (vals[0] >= vals[1]); break;
			case GTHAN: return (vals[0] > vals[1]); break;
		}
	}

	/*evaluate two values*/
	int short_int_evaluate (int a, int b, int op) {
		switch (op) {
			case ADD: return (a + b); break;
			case SUBTRACT: return (a - b); break;
			case MULTIPLY: return (a * b); break;
			case DIVIDE: return (a / b); break;
			case LTHAN: return (a < b); break;
			case LTHANE: return (a <= b); break;
			case EQUALS: return (a == b); break;
			case NEQUALS: return (a != b); break;
			case GTHANE: return (a >= b); break;
			case GTHAN: return (a > b); break;
		}
	}

	/*concatonate two constant char*/
	char* short_concatanate (char* a, char* b) {
		/*safe way to avoid overwriting anything*/
		int length = strlen(a) + strlen(b) + 2;
		char *new = (char*) malloc (length);

		memcpy (new,a,strlen(a));
		*(new+strlen(a)) = ' ';
		memcpy (new+strlen(a)+1,b,strlen(b));
		*(new+length-1)= '\0';
		return new;
	}

	char* concatanate (struct param_list params) {
		char* vals[2];
				
		/* first param can be string literal or string var or name accessor */
		if (params.list[0].is_name) {
			vals[0] = (char*) get_address_size((char*) params.list[0].value, CSTRING)->addr;
		}
		else if (params.list[0].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[0].value;
			if (accsr->list[1].value != FNAME) {yyerror("First operand to string concatenation must be of type string");exit(-1);}
			else {vals[0] = ((cooltype*) get_address_size((char*) accsr->list[0].value, FGROUP)->addr)->name;}
		}			
		else {
			vals[0] = (char*) params.list[0].value;
		}

		/*second param can be string literal or int literal or int var or string var or string or int accessor*/
		if (params.list[1].is_name) {
			int type = get_type ((char*) params.list[1].value);
			switch (type) {
				case CSTRING: vals[1] = (char*) get_address_size((char*) params.list[1].value, CSTRING)->addr; break;
				case INTEGER: vals[1] = itoa (*( (int*) get_address_size((char*) params.list[1].value, INTEGER)->addr)); break;
				default:  yyerror ("Invalid type for + operator.\n"); exit(-1); break;
			}
		}
		else if (params.list[1].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[1].value;
			switch (accsr->list[1].value) {
				case FNAME: vals[1] = ((cooltype*) get_address_size((char*) accsr->list[0].value, FGROUP)->addr)->name; break;
				case FQUANTITY: vals[1] = itoa(((cooltype*) get_address_size((char*) accsr->list[0].value, FGROUP)->addr)->quantity); break;
				case FNUTRITION: vals[1] = itoa(((cooltype*) get_address_size((char*) accsr->list[0].value, FGROUP)->addr)->nutrition); break;
				case FTIME: vals[1] = itoa(((cooltype*) get_address_size((char*) accsr->list[0].value, FGROUP)->addr)->time); break;				
			}
		}
		else {
			switch (params.list[1].type) {
				case CSTRING: vals[1] = (char*) params.list[1].value; break;
				case INTEGER:	vals[1] = itoa(params.list[1].value); break;
				default: yyerror ("Illegal type in + expression.\n"); exit (-1); break;
			}
		}

		/*concat the strings*/
		return short_concatanate (vals[0], vals[1]);
	}
			


		

/*______________________________________________statement functions____________________________________________*/

	/* the params list is only a list of id names - this cycles through them - sees if they exist in the symbol table
	- and if not adds them */
	void int_declare (struct param_list params) {
		int i = 0;
		for (i;i<params.num_params;i++) {
			if( table_includes(symboltable, (char*) params.list[i].value)->index == -1) {
				int* temp = (int*) malloc (sizeof (int));
				table_insert(symboltable, (char*) params.list[i].value, INTEGER, (void*) temp, sizeof(int));
				free (temp);
			}
			else {
				yyerror("Compile-Time Error");
				printf("Variable %s already defined.\n", params.list[i].value);
				exit(-1);
			}
		}
	}

	/*same as ints just for strings - these could be combined and use a switch statement instead*/
	void string_declare (struct param_list params) 
	{
		int i = 0;
		for (i;i<params.num_params;i++) {
			if( table_includes(symboltable, (char*) params.list[i].value)->index == -1) {
				char* temp = (char*) malloc (1);
				*temp = '\0';
				table_insert(symboltable, (char*) params.list[i].value, CSTRING, (void*) temp, 1);
				free (temp);
			}
			else {
				yyerror("Compile-Time Error");
				printf("Variable %s already defined.\n", params.list[i].value);
				exit(-1);
			}
		}
	}

	void fgroup_declare (struct param_list params)
	{ 
		int i;
		char* name = (char*) params.list[params.num_params-1].value;

		for (i = 0;i<params.num_params - 1;i++) {
			if( table_includes(symboltable, (char*) params.list[i].value)->index == -1) {
				/*get a pointer to the defined type with the right name */
				rh_address *temp = table_retrieve(nutritiontable, table_includes(nutritiontable, name)->index);
				cooltype *c_temp = (cooltype*) temp->addr;
				
				/*this changes the name in the nutritiontable - but I don't really care*/
				c_temp->name = (char*) params.list[i].value;

				table_insert(symboltable, (char*) params.list[i].value, FGROUP, (void*) c_temp, sizeof (cooltype));
			}
			else {
				yyerror("Compile-Time Error");
				printf("Variable %s already defined.\n", params.list[i].value);
				exit(-1);
			}
		}
	}

	/*the param list has two parameters.  The first is the ID to assign to - in the form of a name - and the second is the thing to assign to it
	this could be either a literal, a name, or an operation.  what it is can be determined with .is_name, .is_addr and .is_expr fields */

	void assign (struct param_list params)
	{
		/*first checks the type of the left side - and checks that it is a valid location*/
		int lh_type = get_type((char*) params.list[0].value);
		int rh;
		struct expression* expr;
			
		/*as of now - if it's an expression - then it can only be an int - can't assign a string concat to a string */
		if (params.list[1].is_expr) {
			switch (lh_type) {
				case INTEGER: expr = (struct expression*) params.list[1].value;
						rh = int_evaluate (expr->p_list, expr->op);

						/*tried to assign a string concat*/
						if (rh == -1) {
							yyerror ("string concatanations cannot be assigned to ints\n");
							exit (-1);
						}

						table_assign (symboltable, (char*) params.list[0].value, &rh, sizeof(int)); break;

				case CSTRING: yyerror ("compile-time error.  only string variables or string literals may be assigned to strings.\n");
						exit (-1);
						break;
			}
		}
		else if (params.list[1].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[1].value;
			switch (accsr->list[1].value) {
				case FNAME:	if (lh_type != CSTRING) {yyerror ("Can't assign a name to an int"); exit(-1);}
						else {
							struct rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP);
							table_assign (symboltable, (char*) params.list[0].value, ((cooltype *) rh_addr->addr)->name, strlen (((cooltype *) rh_addr->addr)->name));
						} break;
				case FQUANTITY:   	if (lh_type != INTEGER) {yyerror ("Can't assign quantity to non-int variable"); exit(-1);}
							else {
								struct rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP);
								table_assign (symboltable, (char*) params.list[0].value, &((cooltype *) rh_addr->addr)->quantity, sizeof (int));
							} break;	
				case FNUTRITION:	if (lh_type != INTEGER) {yyerror ("Can't assign quantity to non-int variable"); exit(-1);}
							else {
								struct rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP);
								table_assign (symboltable, (char*) params.list[0].value, &((cooltype *) rh_addr->addr)->nutrition, sizeof (int));
							} break;
				case FTIME:		if (lh_type != INTEGER) {yyerror ("Can't assign quantity to non-int variable"); exit(-1);}
							else {
								struct rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP);
								table_assign (symboltable, (char*) params.list[0].value, &((cooltype *) rh_addr->addr)->time, sizeof (int));
							} break;
			}
		}
		
		/*so this is a variable that has to agree*/
		else if (params.list[1].is_name) {	
			rh_address* rh_addr = get_address_size ((char*) params.list[1].value, lh_type);
			switch (lh_type) {
				case INTEGER:  table_assign (symboltable, (char*) params.list[0].value, rh_addr->addr, rh_addr->size); break;
				case CSTRING:	 table_assign (symboltable, (char*) params.list[0].value, rh_addr->addr, rh_addr->size); break;
			}	
		}
			
		/*a literal - again - types have to agree*/
		else {
			if (lh_type != params.list[1].type) {
				yyerror("Compile-Time Error");
				printf("Type mismatch with regards to variable %s.\n", params.list[0].value);
				exit(-1);
			}
			else {
				switch (lh_type) {
					case INTEGER:  table_assign (symboltable, (char*) params.list[0].value, &params.list[1].value, sizeof(int)); break;
					case CSTRING:	 table_assign (symboltable, (char*) params.list[0].value, (char*) params.list[1].value, strlen((char*) params.list[1].value)); break;
				}
			}
		}
	}

	void assign_quantity (struct param_list params)
	{
		rh_address *rh_addr = get_address_size ((char*) params.list[0].value, FGROUP);
		int* q_addr = &((cooltype*) rh_addr->addr)->quantity;

		if (params.list[1].is_name) {
			rh_address* rh_addr = get_address_size ((char*) params.list[1].value, INTEGER);
			*q_addr = *( (int*) rh_addr->addr);
		}
		else if (params.list[1].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[1].value;
			rh_address* rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP);
			switch (accsr->list[1].value) {
				case FNAME: yyerror("cannot assign a string variable to quantity field"); exit (-1); break;
				case FQUANTITY:  *q_addr = ((cooltype*) rh_addr->addr)->quantity; break;
				case FNUTRITION:  *q_addr = ((cooltype*) rh_addr->addr)->nutrition; break;
				case FTIME: *q_addr = ((cooltype*) rh_addr->addr)->time; break;				
			}

			}
		else {
			*q_addr = params.list[1].value;
		}
	}

	
	/*the first param is the condition - which might be a number and might be an expression.  The second
	is a pointer to the statement list */

	void simple_if (struct param_list params)
	{
		char truth;

		/*get the value of the condition*/
		if (params.list[0].is_expr) {
			struct expression* temp = (struct expression*) params.list[0].value;
			truth = int_evaluate (temp->p_list, temp->op);
		}
		else {
			truth = params.list[0].value;
		}

		/*error if string expression*/
		if (truth == -1) {
			yyerror ("string expressions cannot be used conditionals\n");
			exit (-1);
		}

		/*call execute on the list of statements */
		if (truth) {
			struct statement_list* temp = (struct statement_list*) params.list[1].value;
			execute (*temp);
		}
	}
	
	/*third parameter in list is the else statements list*/	
	void if_then_else (struct param_list params) {
		char truth;

		/*get the value of the condition*/
		if (params.list[0].is_expr) {
			struct expression* temp = (struct expression*) params.list[0].value;
			truth = int_evaluate (temp->p_list, temp->op);
		}

		else {
			truth = params.list[0].value;
		}

		/*error if string expression*/
		if (truth == -1) {
			yyerror ("string expressions cannot be used as conditionals\n");
			exit (-1);
		}

		/*call execute on the list of statements */
		if (truth) {
			struct statement_list* temp = (struct statement_list*) params.list[1].value;
			execute (*temp);
		}
		/*or on the other list of statements*/
		else {
			struct statement_list* temp = (struct statement_list*) params.list[2].value;
			execute (*temp);
		}
	}

	/*same as the simple_if except that it loops */
	void while_loop (struct param_list params) {
		char truth = 1;

		while (truth) {
		
			/*get the value of the condition*/
			if (params.list[0].is_expr) {
				struct expression* temp = (struct expression*) params.list[0].value;
				truth = int_evaluate (temp->p_list, temp->op);
			}

			else {
				truth = params.list[0].value;
			}

			/*error if string expression*/
			if (truth == -1) {
				yyerror ("string expressions cannot be used as conditionals\n");
				exit (-1);
			}

			/*call execute on the list of statements */
			if (truth) {
				struct statement_list* temp = (struct statement_list*) params.list[1].value;
				execute (*temp);
			}
		}
	}

	/*first param could be value or address - second param is the execution list */
	void repeat_loop (struct param_list params) {
		int num_times;
		int i=0;

		/*this inherently tests that it is an int*/
		if (params.list[0].is_name) {
			struct rh_address *rh_addr = get_address_size ((char*) params.list[0].value, INTEGER);
			num_times = *((int*) rh_addr->addr);	
		}
		else if (params.list[0].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[0].value;
			rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP);
			switch (accsr->list[1].value) {
				case FNAME:	yyerror ("repeat statement takes int, not string"); exit(-1);break;
				case FQUANTITY:	num_times = ((cooltype *) rh_addr->addr)->quantity; break;
				case FNUTRITION:	num_times = ((cooltype *) rh_addr->addr)->nutrition; break;
				case FTIME:		num_times = ((cooltype *) rh_addr->addr)->time; break;
			}
		}			
		else {
			num_times = params.list[0].value;
		}

		for (i;i<num_times;i++) {
			struct statement_list* temp = (struct statement_list*) params.list[1].value;
			execute (*temp);
		}
	}

	void print (struct param_list params) {
		if (params.list[0].is_expr) {
			
			struct expression *expr = (struct expression*) params.list[0].value;
			int ev = int_evaluate (expr->p_list, expr->op);

			/*wasn't string concat*/
			if (ev != -1) {
				yyerror ("print only takes string or string expressions.\n");
				exit (-1);
			}
			/*might be string concat - second type still need checked*/
			else {
				char *to_print = concatanate (expr->p_list);
				printf ("%s\n", to_print);
			}
		}
		else if (params.list[0].is_accsr) {
			struct param_list *tlist = (struct param_list*) params.list[0].value;
			
			if (tlist->list[1].value != FNAME) {yyerror ("accessors other than name may not be printed in this fashion"); exit(-1);}

			struct rh_address *rh_addr = get_address_size ((char*) tlist->list[0].value, FGROUP);
			printf ("%s\n", ((cooltype*) rh_addr->addr)->name);
		}	
		else if (params.list[0].is_name) {
			struct rh_address *rh_addr = get_address_size ((char*) params.list[0].value, CSTRING);
			printf ("%s\n", rh_addr->addr);
			}
		else {
			printf ("%s\n", params.list[0].value);
		}
	}
	/*cooking functions*/
	bake (cooltype* object, int num_times)
	{
		printf ("bake - %s - %d minutes.\n", object->name, num_times);	
	}
	chop (cooltype* object, int num_times) 
	{
		printf ("chop - %s - %d minutes.\n", object->name, num_times);	
	}
	boil (cooltype* object, int num_times) 
	{
		printf ("boil - %s - %d minutes.\n", object->name, num_times);	
	}
	grill (cooltype* object, int num_times) 
	{
		printf ("grill - %s - %d minutes.\n", object->name, num_times);	
	}
	fry (cooltype* object, int num_times) 
	{
		printf ("fry - %s - %d minutes.\n", object->name, num_times);	
	}
	

	/*we want a control function so that its easier to deal with which of the different functions work on each variables
	- this could also be done in the individual functions*/
	void method (struct param_list params)
	{
		/*get_address of food type*/
		rh_address *lh_addr = get_address_size ((char*) params.list[0].value, FGROUP);
		
		/*evaluate the number*/
		int num_times = 0;
		if (params.list[2].is_name) {
			struct rh_address *rh_addr = get_address_size ((char*) params.list[2].value, INTEGER);
			num_times = *((int*) rh_addr->addr);	
		}
		else if (params.list[2].is_accsr) {
			struct param_list *accsr = (struct param_list*) params.list[2].value;
			rh_address *rh_addr = get_address_size ((char*) accsr->list[0].value, FGROUP);
			switch (accsr->list[1].value) {
				case FNAME:	yyerror ("Cooking functions don't take strings as parameters"); exit(-1);break;
				case FQUANTITY:	num_times = ((cooltype *) rh_addr->addr)->quantity; break;
				case FNUTRITION:	num_times = ((cooltype *) rh_addr->addr)->nutrition; break;
				case FTIME:		num_times = ((cooltype *) rh_addr->addr)->time; break;
			}
		}			
		else {
			num_times = params.list[2].value;
		}

		
		/*redirect to appropriate function*/
		if (! strncmp((char*) params.list[1].value, "boil", 4)) {
			boil ( ((cooltype*) lh_addr->addr), num_times);
		}
		else if (! strncmp((char*) params.list[1].value, "chop", 4)) {
			chop ( ((cooltype*) lh_addr->addr), num_times);
		}
		else if (! strncmp((char*) params.list[1].value, "bake", 4)) {
			bake ( ((cooltype*) lh_addr->addr), num_times);
		}
		else if (! strncmp((char*) params.list[1].value, "grill", 5)) {
			grill ( ((cooltype*) lh_addr->addr), num_times);
		}
		else if (! strncmp((char*) params.list[1].value, "fry", 3)) {
			fry ( ((cooltype*) lh_addr->addr), num_times);
		}
	}


		 

		

int yywrap() { 
#ifdef DEBUG
	printf(" < ==== S y m b o l   T a b l e ==== > \n");
	table_print(symboltable);
#endif
return 1; 
} 

int main(int argc, char *argv[]) {
  if( argc != 2 ) {
	printf("Usage: coolinary [filename]\n");
	exit(-1);
  }

  coolinary_init();
  yyin = fopen(argv[1], "r");
  if( yyin == NULL ) {
	printf("coolinary: no input files.\n");
	exit(-2);
  }
  yyparse();
  fclose(yyin);
  return(0);
}

%}
%token OBRACE EBRACE OPAREN EPAREN SEMI COMMA
%token GROUP PRINT INT STRING IF THEN ELSE WHILE REPEAT NAME QUANTITY NUTRITION TIME
%token EQ NE LT LE GT GE PLUS MINUS INC DEC ASSIGN DOT OVER TIMES ADDFUNC
%token FTYPE MNAME ID ILIT SLIT 

%union {
	int num;
	char* text;
	struct param	param;
	struct param_list plist;
	struct id_list idlist;
	struct statement stmt;
	struct statement_list slist;
};

%type <num> ILIT arith_op rel_op
%type <text> FTYPE MNAME SLIT ID
%type <param> base_type arith_expr relational_expr string_accessor int_accessor
%type <plist> postfix_expr
%type <idlist> decl_list
%type <stmt> stmt decl_stmt assn_stmt expr_stmt selection_stmt iteration_stmt print_stmt method_stmt
%type <slist> stmt_list compound_stmt

%%

program: /* empty */
	| compound_stmt		{execute($1);}
	;

compound_stmt:	OBRACE stmt_list EBRACE	{$$ = $2;}
	

stmt_list:	stmt				{$$.num_statements = 1; $$.s_list[0] = $1;}
		| stmt_list stmt	 	{$$.num_statements = $1.num_statements + 1; $$.s_list[$1.num_statements] = $2;}
       	;

stmt:		decl_stmt			{$$ = $1;}
		| assn_stmt			{$$ = $1;}
		| expr_stmt			{$$ = $1;}
		| selection_stmt		{$$ = $1;}
		| iteration_stmt		{$$ = $1;}
		| print_stmt			{$$ = $1;}
		| method_stmt			{$$ = $1;}
       	;

decl_stmt:	INT decl_list	SEMI		{$$.fpointer = int_declare; $$.p_list = ids_to_params ($2);}
	    	| STRING decl_list SEMI	{$$.fpointer = string_declare; $$.p_list = ids_to_params ($2);}
	    	| FTYPE decl_list SEMI	{$$.fpointer = fgroup_declare; $$.p_list = ids_to_params ($2);
						$$.p_list.list[$$.p_list.num_params].value = (int) $1; $$.p_list.num_params++;}
	    	| GROUP decl_list SEMI
		| ID decl_list SEMI
		;

decl_list:	ID				{$$.num_ids = 1; $$.list[0] = strdup($1);}
		| decl_list COMMA ID 	{$$.num_ids = $1.num_ids + 1; $$.list[$1.num_ids] = strdup($3);}
		;

assn_stmt:	ID ASSIGN base_type SEMI	{$$.fpointer = assign; $$.p_list.list[0].value = (int) $1; $$.p_list.list[1] = $3;}
		| ID ASSIGN arith_expr SEMI		{$$.fpointer = assign; $$.p_list.list[0].value = (int) $1; $$.p_list.list[1] = $3;}
		| ID DOT QUANTITY ASSIGN base_type SEMI	{if (! $5.is_name && ! $5.is_accsr && $5.type != INTEGER) {
									yyerror ("Only integer values can be assigned to a quantity field"); exit(-1);
								}
								$$.fpointer = assign_quantity; $$.p_list.list[0].value = (int) $1; $$.p_list.list[1] = $5;}
		;

expr_stmt:	postfix_expr SEMI			{$$.fpointer = assign; $$.p_list = $1;}
		;

selection_stmt:	IF OPAREN relational_expr EPAREN compound_stmt					{$$.fpointer = simple_if; $$.p_list.list[0] = $3;
														$$.p_list.list[1].value = (int) &$5;}

			| IF OPAREN relational_expr EPAREN compound_stmt ELSE compound_stmt		{$$.fpointer = if_then_else; $$.p_list.list[0] = $3;
														$$.p_list.list[1].value = (int) &$5; $$.p_list.list[2].value = (int) &$7;}
			;

/*type checking gets done in the repeat_loop function for loops (for the ints) and in the evaluate function for relational_expr's*/
iteration_stmt:	WHILE OPAREN relational_expr EPAREN compound_stmt	{$$.fpointer = while_loop; $$.p_list.list[0] = $3;
											$$.p_list.list[1].value = (int) &$5;} 	

			| REPEAT OPAREN base_type EPAREN compound_stmt		{if ($3.type != INTEGER && $3.type != EMPTY) {
												yyerror ("Repeat only takes integer literals or variables\n");
												exit (-1);
											}
											else
												$$.fpointer = repeat_loop; $$.p_list.list[0] = $3;
												$$.p_list.list[1].value = (int) &$5;
											}	
			;

/*second case is only for string concat - this type checking is done in the print function */
print_stmt:	PRINT OPAREN base_type EPAREN SEMI		{if ($3.type != CSTRING && ! $3.is_name && ! $3.is_accsr) {
									yyerror ("print only takes strings or string expressions\n");
									exit (-1);
								}
								else {
									$$.fpointer = print;
									$$.p_list.list[0] = $3;
								}}
		| PRINT OPAREN arith_expr EPAREN SEMI	{if ($3.type != CSTRING && (! $3.is_name && ! $3.is_expr && ! $3.is_accsr)) {
									yyerror ("print only takes strings or string expressions\n");
									exit (-1);
								}
								else {
									$$.fpointer = print;
									$$.p_list.list[0] = $3;
								}}		
		;

method_stmt:	ID DOT MNAME OPAREN base_type EPAREN SEMI	{if ($5.type == CSTRING) {yyerror ("Cooking functions do not take a string as a parameter"); exit (-1);}
									else {$$.fpointer = method;  $$.p_list.list[0].value = (int) $1; $$.p_list.list[1].value = (int) $3;
									$$.p_list.list[2] = $5;}}


postfix_expr:	ID INC					{$$.list[0].value = (int) $1; $$.list[0].is_name = 1;
							struct expression *temp = (struct expression*) malloc (sizeof (struct expression));
							temp->op = ADD; 
							temp->p_list.list[0] = $$.list[0];
							temp->p_list.list[1].value = 1;  temp->p_list.list[1].is_name = 0; temp->p_list.list[1].type = INTEGER; 
							$$.list[1].value = (int) temp; $$.list[1].is_name = 0; $$.list[1].is_expr = 1;
							}	
							
		|ID DEC				{$$.list[0].value = (int) $1; $$.list[0].is_name = 1;
							struct expression *temp = (struct expression*) malloc (sizeof (struct expression));
							temp->op = SUBTRACT; 
							temp->p_list.list[0] = $$.list[0];
							temp->p_list.list[1].value = 1;  temp->p_list.list[1].is_name = 0; temp->p_list.list[1].type = INTEGER; 
							$$.list[1].value = (int) temp; $$.list[1].is_name = 0; $$.list[1].is_expr = 1;
							}
		;

/*Type check during the evaluation process*/
arith_expr:	base_type arith_op base_type	{	/*start by testing for whether they are variables - where possible evaluate to a constant so that we won't have to
								recalculate in a looping situation*/
								if ( ((!$1.is_name) && (!$1.is_accsr)) && ((!$3.is_name) && (!$3.is_accsr))) {
									if ($1.type == INTEGER && $3.type == INTEGER) {
										$$.value = short_int_evaluate($1.value, $3.value, $2); $$.is_expr = 0; $$.is_name = 0;
										$$.is_accsr = 0; $$.type = INTEGER;
							 		}
									else if ($1.type == CSTRING && $3.type == CSTRING) {
										$$.value = (int) short_concatanate((char*) $1.value, (char*) $3.value); $$.is_expr = 0; $$.is_name = 0; 
										$$.is_accsr = 0; $$.type = CSTRING;
									}
									else if ($1.type == CSTRING && $3.type == INTEGER) {
										$$.value = (int) short_concatanate((char*) $1.value, itoa($3.value)); $$.is_expr = 0; $$.is_name = 0; 
										$$.is_accsr = 0; $$.type = CSTRING;
									}
									else {
										yyerror ("Coolinary does not allow operations on non-integer or string types.\n");
										exit(-1);
									}
								}
								/*turn int type expression - we don't type check here*/
								else {
									struct expression *temp = (struct expression*) malloc (sizeof (struct expression));
									temp->op = $2;
									temp->p_list.list[0] = $1; temp->p_list.list[1] = $3; 
									$$.value = (int) temp; $$.is_name = 0; $$.is_expr = 1; $$.is_accsr = 0; $$.type = EMPTY;									
								}
							}
		;

/*we're don't type check here - evaluate does this*/
relational_expr:	base_type rel_op base_type 		{if ((! $1.is_name && ! $1.is_accsr) && (! $3.is_name && ! $3.is_accsr)) {
									if ($1.type == INTEGER && $3.type == INTEGER) {
										$$.value = short_int_evaluate($1.value, $3.value, $2); $$.is_expr = 0;
										$$.is_accsr = 0; $$.type = EMPTY;
									} 
							 	}
								else {
									struct expression *temp = (struct expression*) malloc (sizeof (struct expression));
									temp->op = $2; 
									temp->p_list.list[0] = $1; temp->p_list.list[1] = $3; 
									$$.value = (int) temp; $$.is_name = 0; $$.is_expr = 1; $$.is_accsr = 0; $$.type = EMPTY;
								}}
			;

/*rename primitive type or something */

		/*ID's have type empty because we need to check the symbol table to get their type - can't do it in the SDD */
base_type:	ID 				{$$.value = (int) $1; $$.is_name = 1; $$.is_expr = 0; $$.is_accsr = 0; $$.type = EMPTY;}
		| ILIT				{$$.value = $1; $$.is_name = 0; $$.is_expr = 0; $$.is_accsr = 0; $$.type = INTEGER;}
		| SLIT 			{$$.value = (int) $1; $$.is_name = 0; $$.is_expr = 0; $$.is_accsr = 0; $$.type = CSTRING;}
		| string_accessor 		{$$ = $1;}
		| int_accessor		{$$ = $1;}
		;

string_accessor:	ID DOT NAME 		{struct param_list *new = (struct param_list*) malloc (sizeof (struct param_list));
						new->list[0].value = (int) $1; new->list[1].value = FNAME; $$.value = (int) new; $$.is_accsr = 1;
						$$.is_name = 0; $$.is_expr = 0; $$.type = EMPTY;}
			;

int_accessor:		ID DOT QUANTITY 	{struct param_list *new = (struct param_list*) malloc (sizeof (struct param_list));
						new->list[0].value = (int) $1; new->list[1].value = FQUANTITY; $$.value = (int) new; $$.is_accsr = 1;
						$$.is_name = 0; $$.is_expr = 0; $$.type = EMPTY;}
			|ID DOT NUTRITION 	{struct param_list *new = (struct param_list*) malloc (sizeof (struct param_list));
						new->list[0].value = (int) $1; new->list[1].value = FNUTRITION; $$.value = (int) new; $$.is_accsr = 1;
						$$.is_name = 0; $$.is_expr = 0; $$.type = EMPTY;}
			|ID DOT TIME		{struct param_list *new = (struct param_list*) malloc (sizeof (struct param_list));
						new->list[0].value = (int) $1; new->list[1].value = FTIME; $$.value = (int) new; $$.is_accsr = 1;
						$$.is_name = 0; $$.is_expr = 0; $$.type = EMPTY;}
			;

arith_op:	PLUS			{$$ = ADD;}
		| MINUS		{$$ = SUBTRACT;}
		| TIMES		{$$ = MULTIPLY;}
		| OVER			{$$ = DIVIDE;}
		;

rel_op:	LT			{$$ = LTHAN;}
		|LE			{$$ = LTHANE;}
		|EQ			{$$ = EQUALS;}
		|NE			{$$ = NEQUALS;}
		|GE			{$$ = GTHANE;}
		|GT			{$$ = GTHAN;}
		;